package com.atguigu.gmall.index.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @description:
 * @author: xie.zh
 * @create: 2021.03.23   14:39
 * @version: 1.0
 */
@Component
public class DistributeLock {
    @Autowired
    private StringRedisTemplate redisTemplate;

    private Timer timer;

    public Boolean tryLock(String lock, String uuid, Integer expire) {
        String script = "if(redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],KEYS[2])==1) then " +
                "redis.call('hincrby',KEYS[1],KEYS[2],1) redis.call('expire',KEYS[1],ARGV[1]) return 1 else return 0 end";
        Boolean execute = this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),
                Arrays.asList(lock, uuid), expire.toString());
        if (!execute) {
            try {
                Thread.sleep(100);
                this.tryLock(lock, uuid, expire);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.renewExpire(lock, uuid, expire);
        return true;
    }

    public void unLock(String lock, String uuid) {
        String script = "if(redis.call('hexists',KEYS[1],KEYS[2]) ==0) then return nil elseif(redis.call('hincrby'," +
                "KEYS[1],KEYS[2],-1)==0) then return redis.call('del',KEYS[1]) else return 0 end";
        Long execute = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lock, uuid));
        if (execute == null) {
            throw new RuntimeException("要解的锁不存在，或者不属于你");
        } else if (execute == 1) {
            timer.cancel();
        }
    }

    public void renewExpire(String lock, String uuid, Integer expire) {
        String script = "if(redis.call('hexists',KEYS[1],KEYS[2])==1) then return redis.call('expire',KEYS[1],ARGV[1]) else return 0 end";
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lock, uuid),
                        expire.toString());
            }
        }, expire / 3 * 1000, expire / 3 * 1000);
    }
}
